package sicnu.cs.aps.service.impl;


import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import static sicnu.cs.aps.common.Constants.*;
import static sicnu.cs.aps.common.util.ValidateUtils.*;

import org.springframework.web.multipart.MultipartFile;
import sicnu.cs.aps.common.Constants;
import sicnu.cs.aps.common.util.RequestUtil;
import sicnu.cs.aps.common.util.SnowflakeIdWorkerUtil;
import sicnu.cs.aps.common.util.excel.ExcelUtils;
import sicnu.cs.aps.domain.dto.StudentImportDto;
import sicnu.cs.aps.domain.entity.Role;
import sicnu.cs.aps.domain.entity.StudentInfo;
import sicnu.cs.aps.domain.entity.User;
import sicnu.cs.aps.mapper.*;
import sicnu.cs.aps.service.IUserService;

import java.util.*;
import java.util.stream.Collectors;


/**
 * @author CaiKe
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements IUserService {

    private final RoleMapper roleMapper;
    private final UserMapper userMapper;
    private final SubAdminMapper subAdminMapper;
    private final UserInfoMapper userInfoMapper;
    private final PasswordEncoder passwordEncoder;
    private final TeacherInfoMapper teacherInfoMapper;
    private final StudentInfoMapper studentInfoMapper;

    /**
     * 注册用户
     *
     * @param user       用户信息
     * @param targetRole 角色信息
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public void registUser(User user, String targetRole) {
        roleMapper.findOptionalByAuthority(targetRole)
                .ifPresentOrElse(
                        role -> {
                            var userToSave = user
                                    .withPassword(passwordEncoder.encode(user.getPassword()))
                                    .withAuthorities(Set.of(role));
                            userMapper.insert(userToSave);
                            roleMapper.addUserRole(userToSave.getId(), role.getId());
                            // 按照不同身份，初始化不同表
                            createUserInfo(userToSave.getId(), role.getAuthority());
                        },
                        () -> {
                            throw new NoSuchElementException("No Such Role!");
                        }
                );
    }

    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.MANDATORY)
    public void createUserInfo(Long id, String authority) {
        if (ROLE_STUDENT.equals(authority)) {
            userInfoMapper.createStudentInfo(id);
        } else switch (authority) {
            case (ROLE_TEACHER):
                userInfoMapper.createTeacherInfo(id);
                break;
            case (ROLE_UNIVERSITY):
                userInfoMapper.createCollegeInfo(id);
                break;
            default:
                break;
        }
    }

    @Override
    public boolean isUsernameExisted(String username) {
        return userMapper.countByUsername(username) > 0;
    }

    @Override
    public boolean isEmailExisted(String email) {
        return userMapper.countByEmail(email) > 0;
    }

    @Override
    public boolean isMobileExisted(String mobile) {
        return userMapper.countByMobile(mobile) > 0;
    }

    @Override
    public boolean isUserIdExisted(Long id) {
        return userMapper.countByUserId(id) > 0;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public boolean resetPassword(Long id, String password) {
        if (isValidPassword(password)) {
            User userToUpdate = User.builder()
                    .id(id)
                    .password(passwordEncoder.encode(password))
                    .build();
            userMapper.updateById(userToUpdate);
            return true;
        }
        return false;
    }


    @Override
    public User findUserByEmail(String email) {
        return userMapper.findOptionalByEmail(email).orElseThrow(() -> {
            throw new NoSuchElementException("没有找到用户");
        });
    }

    /**
     * 查找用户
     *
     * @param userId 主键
     * @return 用户
     */
    @Override
    public User findUser(Long userId) {
        return userMapper.findOptionalByUserId(userId).orElseThrow(() -> {
            throw new NoSuchElementException("没有找到用户");
        });
    }

    @Override
    public boolean updateEmail(Long userId, String email) {
        return userMapper.updateEmail(userId, email) > 0;
    }

    @Override
    public String findPassword(Long userId) {
        return userMapper.findPassword(userId);
    }

    /**
     * 用户导入服务
     *
     * @param file 文件流
     * @param type 导入用户类型
     * @return List<String>，下标为0的串为错误信息或者提示信息，如有未导入成功的数据则从下标 1开始
     * @author walnut
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<String> importUser(MultipartFile file, Integer type) throws Exception {
        //导入状态
        int flag;
        //导入用户
        List<String> returnInfo = new ArrayList<>();
        if (type.equals(USER_IMPORT_STUDENT)) {
            //将excel文件解析，转换为excelDto
            List<StudentImportDto> studentImportDtoList = ExcelUtils.readMultipartFile(file, StudentImportDto.class);
            //用户信息
            List<User> users = new ArrayList<>();
            //插入学生信息
            List<StudentInfo> studentInfos = new ArrayList<>();
            try {
                studentImportDtoList.forEach(
                        dto -> {
                            //生成统一id
                            //                        Long nextId = SnowflakeIdWorkerUtil.getNextId();
                            Long nextId = Long.valueOf(dto.getSid());
                            //转换为用户信息信息
                            User user = dto.convertUser();
                            //插入统一id
                            user.setId(nextId);
                            users.add(user);
                            //转换为学生信息
                            StudentInfo studentInfo = dto.convertStudentInfo();
                            studentInfo.setUserId(nextId);
                            studentInfos.add(studentInfo);
                        }
                );
            } catch (Exception e) {
                return null;
            }

            //获取插入前所有的用户id TODO 完善 添加限制条件时间戳减少查询压力
            List<Long> idsBeforeInsertUser = userMapper.selectId();
            //插入用户数据
            log.info("=============开始导入学生用户数据=============");
            int successInsertingUserInfoNum = userMapper.insertBranch(users);
            log.info("✅用户数据导入数据库成功行数:" + successInsertingUserInfoNum);
            log.info("❌用户数据导入数据库失败行数:" + (users.size() - successInsertingUserInfoNum));
            //获取插入后所有的用户id

            //判断是否全部插入成功
            boolean fail = (successInsertingUserInfoNum < users.size());
            List<StudentInfo> successInsertingUsersExcelStuInfo;
            List<Long> newId = null;
            if (fail) {
                //未全部插入成功，获取插入后的所有用户id
                log.warn("未能将数据全部插入！");
                List<Long> idsAfterInsertUser = userMapper.selectId();
                //获得需要继续操作的数据
                idsAfterInsertUser.removeAll(idsBeforeInsertUser);
                newId = idsAfterInsertUser;
                successInsertingUsersExcelStuInfo = studentInfos.stream().filter(studentInfo -> idsAfterInsertUser.contains(studentInfo.getUserId())).collect(Collectors.toList());
            } else {
                successInsertingUsersExcelStuInfo = studentInfos;
            }
            List<Long> successInsertingStuExcelIds = null;
            //插入学生信息表数据
            if (!fail || successInsertingUsersExcelStuInfo.size() != 0) {

                //进行学生数据的插入
                int successInsertingStuInfoNum = studentInfoMapper.insertBranch(fail ? successInsertingUsersExcelStuInfo : studentInfos);
                log.info("✅学生信息导入数据库成功行数:" + successInsertingStuInfoNum);
                int errLine = successInsertingUserInfoNum - successInsertingStuInfoNum;
                log.info("❌学生信息导入数据库失败行数:" + errLine);

                List<Long> idsAfterInsertStudentInfo = studentInfoMapper.selectId();

                if (errLine != 0) {
                    //有未插入的数据
                    //获得需要继续操作的数据
                    List<Long> successInsertingStuInfoIds = successInsertingUsersExcelStuInfo.stream().map(StudentInfo::getUserId).filter(idsAfterInsertStudentInfo::contains).collect(Collectors.toList());
                    log.info("需要继续操作的数据 ==> " + successInsertingStuInfoIds);
                    newId = successInsertingStuInfoIds;
                    //删除导入学生信息未成功的的用户信息
                    log.info("删除导入学生信息未成功的的用户信息");
                    List<Long> toDelete = successInsertingUsersExcelStuInfo.stream().map(StudentInfo::getUserId).collect(Collectors.toList());
                    toDelete.removeAll(successInsertingStuInfoIds);
                    int delNum = userMapper.deleteBatchIds(toDelete);
                    if (delNum < toDelete.size()) {
                        log.error("删除导入学生信息未成功的的用户信息时发生错误");
                        throw new Exception("删除导入学生信息未成功的的用户信息时发生错误");
                    }
                } else {
                    //无未插入的数据
                    successInsertingStuExcelIds = successInsertingUsersExcelStuInfo.stream().map(StudentInfo::getUserId).collect(Collectors.toList());
                    log.info("需要继续操作的数据 ==> " + successInsertingStuExcelIds);
                    newId = successInsertingStuExcelIds;
                }

            } else {
                log.info("无学生信息导入");
            }

            //插入用户角色表数据
            List<Long> finalNewId = newId;
            roleMapper.findOptionalByAuthority("ROLE_STUDENT")
                    .ifPresentOrElse(
                            role -> {
                                ArrayList<Long> userId = new ArrayList<>();
                                System.out.println(userId);
                                if (finalNewId.size() != 0) {
                                    Integer successNum = roleMapper.insertBranchUserRole(finalNewId, Long.valueOf(role.getId()));
                                    log.info("✅用户角色导入数据库成功行数:" + successNum);
                                    if (successNum != finalNewId.size()) {
                                        log.error("❌用户角色导入数据库失败行数:" + (finalNewId.size() - successNum));
                                        throw new NoSuchElementException("非预期异常:插入用户角色表,错误行数:" + (finalNewId.size() - successNum));
                                    }
                                } else {
                                    log.info("无用户角色导入数据库");
                                }
                            },
                            () -> {
                                throw new NoSuchElementException("No Such Role!");
                            }
                    )
            ;
            //获取未能导入的用户姓名数据用于返回
            List<Long> finalSuccessInsertingStuExcelIds = successInsertingStuExcelIds;
            if (finalSuccessInsertingStuExcelIds == null) {
                flag = Constants.USER_IMPORT_STATE_FAIL;
                List<String> collect = users.stream().map(User::getUsername).collect(Collectors.toList());
                returnInfo.add(String.valueOf(flag));
                returnInfo.addAll(1, collect);
            } else {
                List<String> collect = users.stream()
                        .map((User::getUsername))
                        .filter(username -> !finalSuccessInsertingStuExcelIds.contains(username))
                        .collect(Collectors.toList());
                if (users.size() == finalSuccessInsertingStuExcelIds.size()) {
                    flag = USER_IMPORT_STATE_SUCCESS;
                    returnInfo.add(String.valueOf(flag));
                } else {
                    flag = USER_IMPORT_STATE_PARTIAL_SUCCESS;
                    returnInfo.add(String.valueOf(flag));
                }
                returnInfo.addAll(collect);
            }

            log.warn("导入失败的用户:" + returnInfo);

        } else if (type.equals(USER_IMPORT_TEACHER)) {

        } else {
            return null;
        }
        return returnInfo;
    }

    /**
     * 获取当前用户的学院信息
     *
     * @return 学院id
     */
    @Override
    public Long getCollegeIdByUserId() {
        if (RequestUtil.isCurrentUserACollege()) {
            //学院账户
            if (RequestUtil.isCurrentUserAChild()) {
                //学院子账户
                return subAdminMapper.getFatherIdById(RequestUtil.getCurrentUserId());
            } else {
                //学院顶级账户
                return RequestUtil.getCurrentUserId();
            }

        } else if (RequestUtil.isCurrentUserAUniversity()) {
            //学校账户
            log.error("亲，身份为学校，没有所属的学院哦");
            return null;
        } else if (RequestUtil.isCurrentUserTeacherOrStu()) {
            //老师学生
            if (RequestUtil.isCurrentUserAStu()) {
                //学生账户
                return studentInfoMapper.selectById(RequestUtil.getCurrentUserId()).getCollegeId();
            } else {
                //老师账户
                return teacherInfoMapper.selectById(RequestUtil.getCurrentUserId()).getCollegeId();
            }
        } else {
            log.error("用户身份不对劲🤨");
            return null;
        }
    }

    /**
     * 获取指定用户的学院信息
     *
     * @param id 指定的用户id
     * @return 学院id
     */
    @Override
    public Long getCollegeIdByUserId(Long id) {
        Set<Role> rolesByUserid = roleMapper.findRolesByUserid(id);
        List<String> collect = rolesByUserid.stream().map(Role::getAuthority).collect(Collectors.toList());
        if (collect.contains(ROLE_STUDENT)) {
            return studentInfoMapper.selectCollegeIdByUserId(id);
        } else if (collect.contains(ROLE_TEACHER)) {
            return teacherInfoMapper.selectById(id).getCollegeId();
        } else if (collect.contains(ROLE_COLLEGE)) {
            return id;
        } else if (collect.contains(ROLE_COLLEGE_SUB)) {
            return subAdminMapper.getFatherIdById(id);
        } else {
            return null;
        }
    }

    @Override
    public List<User> listById(List<Long> studentIds) {
        return userMapper.selectBatchIds(studentIds);
    }
}